//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
/*
 * Boot loader main
 */

#include "config.h"
#include "command.h"
#include "serial.h"
#include "time.h"
#include "main.h"
#include "util.h"
#include "bulverde.h"
#include "net.h"
#include "irq.h"
#include "xmodem.h"
#include "ymodem.h"
#include "mem.h"
#include "tat.h"
#include "version.h"
#include "crc16.h"
#include "usb_serial_download.h"
#include "pxa_usb.h"

/* simon */
#include "pcf50606.h"

/* ethan */

#include "bcm2121.h"
#include "KeypadTest.h"

#define TAT 1
#define USBDOWNLOADIMAGE 1

#define USBCHECKCON 1
#define NOKEYPAD    1
//#define FLASH

//The following is used to update the whole Image by USB

#ifdef TAT
unsigned int run_mode;
unsigned int verbose_level = 1;
#endif
int version_no = BlobBootLoaderVersion;

extern int IdentifyDoc (void);
extern int FormatDoc();
extern int WriteIPL(int addr);
extern int WriteSPL(int addr);
extern int WriteLogo();
extern int ReadLogo();
extern int WriteKernel();
extern int LoadKernel (void *to);
extern int doc_info (void);
extern int WriteRootFs();
extern int WriteMMI();
extern int doc_read_rootfs();
extern int WriteTAT();
extern int ReadTAT();
extern int WriteTatTable(unsigned char *buff);
extern int ReadTatTable(unsigned char *buff);

extern void FastFlash(u32* dest, u32* source, u32 length);
extern int udownload_cmd(u32 *destAddress, u32 bufLen);
extern int md5_stream (char *start_address, int length);
void LoadKernelAndRamdisk(void);
void BootKernel(int arg0, int arg1);
void Download(u8* address);
void Jump(u32* address);
void JumptoTAT(void);
void PrintHelp(void);

void ResetTerminal(void);
void PrintSerialSpeed(eBauds speed);
void ExecuteCommand(u8* commandline);
void hw_reset(void);
int SplitRam(int addr, int nSize);

extern unsigned int  __bss_start;
extern unsigned int  __bss_end;
extern unsigned int  __text;
extern unsigned int  __etext;
extern unsigned int  __data;
extern unsigned int  __edata;

int g_bWrite = 1;

#ifdef TAT
unsigned char rts_signal;
int rts_try;
unsigned long rts_ack_timeout;
#endif

#define CCCR_ADDR 0x41300000

void ShowVersion(void);

void ChangeFreq(int l, int twoN, int turbo, int b)
{
    int value;
    volatile int *regT;

    regT = (volatile int *)CCCR_ADDR;
    value = *regT;
    //make sure the Core and Peripheral PLL disable bits are clear.
    value &= 0x3FFFFFFF;
    value = value & 0xFFFFF000;   //clear the L and 2N bits

    l = l & 0x1F;     //make sure L is only 0 thru 31
    value |= l;      //OR in the L value

    twoN = twoN & 0xf;    //make sure 2N is only 0 thru 15
    value |= (twoN << 7);    //OR in the 2N value

    *regT = value;     //now set the CCCR register

    if (turbo == 0 && b == 0) {
        __asm("mov r0, #0x2");    //set to Frequency Change Mode
        __asm("mcr p14, 0, r0, c6, c0, 0"); //write to the CLKCFG coprocessor reg
    }

    if (turbo != 0 && b == 0) {
        __asm("mov r0, #0x3");    //set to Frequency Change Mode
        __asm("mcr p14, 0, r0, c6, c0, 0"); //write to the CLKCFG coprocessor reg
    }

    if (turbo != 0 && b != 0) {
        __asm("mov r0, #0xB");              //set to Frequency Change Mode
        __asm("mcr p14, 0, r0, c6, c0, 0"); //write to the CLKCFG coprocessor reg
    }
}

void show_symbol_addr(void)
{
    printk("Test \n");
    printk("_text      : %x\n", (unsigned int *)&__text);
    printk("_etext     : %x\n", (unsigned int *)&__etext);
    printk("_data      : %x\n", (unsigned int *)&__data);
    printk("_edata     : %x\n", (unsigned int *)&__edata);
    printk("_bss_start : %x\n", (unsigned int *)&__bss_start);
    printk("_bss_end   : %x\n", (unsigned int *)&__bss_end);
}

void test_bss_data(void)
{
    static unsigned int abc = 0xABCD;

    printk("%x should be 0xabcd when it's called first\n", abc);
    abc++;
}

void ShowUsbPinState(void)
{
    printk("GPIO40 (B_USB_FULLSPEED) : Dir(%d), Level(%d), Alt(%d) \n",
                    (GPDR1 & 0x00000100) ? 1 : 0, (GPLR1 & 0x00000100) ? 1 : 0,
                    (GAFR1_L & 0x00030000) >> 16 );
    printk("GPIO38 (B_OTG_PWREN    ) : Dir(%d), Level(%d), Alt(%d) \n",
                    (GPDR1 & 0x00000040) ? 1 : 0,
                    (GPLR1 & 0x00000040) ? 1 : 0, (GAFR1_L & 0x00003000) >> 12);
    printk("GPIO35 (B_USB_DEC_N    ) : Dir(%d), Level(%d), Alt(%d) \n",
                    (GPDR1 & 0x00000008) ? 1 : 0,
                    (GPLR1 & 0x00000008) ? 1 : 0, (GAFR1_L & 0x000000C0) >> 6);
    printk("GPIO41 (B_OTG_ID       ) : Dir(%d), Level(%d), Alt(%d) \n",
                    (GPDR1 & 0x00000200) ? 1 : 0, (GPLR1 & 0x00000200) ? 1 : 0,
                    (GAFR1_L & 0x000C0000) >> 18);
    printk("GPIO22 (B_OTG_PWRFLG   ) : Dir(%d), Level(%d), Alt(%d) \n",
                    (GPDR0 & 0x00400000) ? 1 : 0, (GPLR0 & 0x00400000) ? 1 : 0,
                    (GAFR0_U & 0x00003000) >> 12);
}

void ArmSys()
{
    unsigned long packsize,flashoffset;
    unsigned long buffer = KERNEL_RAM_START;
    long IsWriteFlash;

    SerialOutputRawByte('0');
    SerialOutputRawByte('1');
    SerialOutputRawByte('2');
    SerialOutputRawByte('3');

    // Send query signal,ask for file length
    SerialOutputRawByte('?');

    //  Get file length nowait
    flashoffset = ReadWordNoWait();
    packsize = ReadWordNoWait();
    IsWriteFlash = packsize & 0x80000000;
    packsize = packsize & ~0x80000000;

    if (packsize) {    // get image from armsys
        int sendsize;

        // end ready signal
        SerialOutputRawByte('<');

        // receive the file data
        ReadBlock((unsigned char*)buffer, packsize);

        SerialOutputRawByte('>');

        // jump to run program if u don't need write flash
        if (IsWriteFlash) {
            printk("Write kernel to DOC\n");
            if (!WriteKernel())
                printk("Write Kernel Failed!\n");
            else
                printk("Have written Kernel to DOC\n");
        }
    }
    else {  // get image from DOC
        SerialOutputRawByte('4');
        SerialOutputRawByte('5');
        SerialOutputRawByte('6');

        if (LoadKernel((void *)KERNEL_RAM_START)) {
            printk("Load Kernel Failed!\n");
            return;
        }
        else
            printk("Load Kernel Successful!\n");
    }

    // image is already in the dram
    BootKernel(0, 0);
}

void c_main(void)
{

#ifdef TAT
    int i;
    unsigned int crc16;
#endif
    unsigned char commandline[128] = {0,};
    int retval = 0;
#ifdef TAT
    unsigned int gpdr0, gafr0_u;
    int ret;
#endif
    /*change frequence to 312/208/104 turbo mode */
    /* Some board doesn't work at high speed */
    ChangeFreq(16, 3, 1, 1); /* 16 * 13MHz = 208 Mhz Turbo-Mode */
    // Initialize
    TimerInit();

//*****Key Press Init U-Boot******
    wls_keypad_init();
    if ('U' == wls_keypad_getkey()) {
        g_bWrite = 0;
        if (UsbDownloadImageEx()) {
            g_bWrite = 1;
            BootKernel(BOOT_ARG0, BOOT_ARG1);
        }
    }
//********************************

    retval = SerialInputBlock_msec(commandline, 1, 5); /* 1 jiffie = 10 milli-sec */
    if (!retval)
        ArmSys();

    ShowVersion();
    printk("Type \"help\" to get a list of commands\n");
    while (1) {
        DisplayPrompt(NULL);

        ret = GetCommand(commandline, 128, 3600);
        if (ret > 0) {
            ExecuteCommand(commandline); /* parse and execute the input */
        }
        else if (ret == 0 && (commandline[0] == '\n'
                || commandline[0] == '\r')) {
            SerialOutputByte('\n');
        }
    }
}

void ShowVersion()
{
#define BOOTLOADVERSION "1.0.0"
    printk( "\n\tElastos2.0 Bootloader for Paladin\n\tVersion:%s, Time Stamp:%s\n", BOOTLOADVERSION, COMPILE_TIME_STAMP );
}

/*
  Parse and execute a command line
*/
void ExecuteCommand(u8* commandline)
{
    int ReadLocation = 0;
    int NextReadLocation = 0;
    u32 ReadValue[3];
    int ParseResult[3];
    u8  FileName[128];

    if(MyStrCmp(commandline, "elastos", 7)) {
        ReadLocation   = 7;
        ParseResult[0] = ParseNumber(commandline+ReadLocation,
                        &NextReadLocation, &(ReadValue[0]));
        ReadLocation   += NextReadLocation;
        ParseResult[1] = ParseNumber(commandline+ReadLocation,
                        &NextReadLocation, &(ReadValue[1]));
        if((ParseResult[0]!=1)||(ParseResult[1] != 1)) {
            ReadValue[0] = BOOT_ARG0;
            ReadValue[1] = BOOT_ARG1;
        }
        cli();
        if(!LoadKernel(KERNEL_RAM_START)) {
            printk("Load Kernel Successful!\n");
            printk("begin boot kernel!\n");
            BootKernel(ReadValue[0], ReadValue[1]);
        } else {
            printk("Load Kernel Failed!\n");
        }
    } else if(MyStrCmp(commandline, "identifydoc", 11)){
        IdentifyDoc();
    } else if(MyStrCmp(commandline, "formatdoc", 9)){
            if(!FormatDoc())
                printk("Format DiskOnChip Failed!\n");
            else {
                int splAddr;
                printk("Format DiskOnChip Successful!\n");
                MyMemCpyChar(RAM_START, 0xA3C00000, 0x80000);
                if (splAddr = SplitRam(RAM_START, 0x80000)) {
                    if(!WriteIPL(RAM_START)) {
                        printk("Write IPL Failed!\n");
                        return;
                    }
                    else
                        printk("Write IPL Successful!\n");

                    if(!WriteSPL(splAddr)) {
                        printk("Write SPL Failed!\n");
                        return;
                    }
                    else
                        printk("Write SPL Successful!\n");
                }
                else
                    printk("Update Loader Failed\n");
            }
    } else if(MyStrCmp(commandline, "udl", 3)){
        int splAddr;
        int nSize;
        printk("Download loader by Xmodem:");
        nSize = XModemReceive(RAM_START);
        if (nSize != -1) {
            if (splAddr = SplitRam(RAM_START, nSize)) {
                if(!WriteIPL(RAM_START)) {
                    printk("Write IPL Failed!\n");
                    return;
                }
                else
                    printk("Write IPL Successful!\n");

                if(!WriteSPL(splAddr)) {
                    printk("Write SPL Failed!\n");
                    return;
                }
                else
                    printk("Write SPL Successful!\n");
            }
            else
                printk("Update Loader Failed\n");
        } else
            printk("Download Loader Failed");
    } else if(MyStrCmp(commandline, "writekernel", 11)) {
        if(!WriteKernel())
            printk("Write Kernel Failed!\n");
        else
            printk("Write Kernel Successful!\n");
    } else if(MyStrCmp(commandline, "loadkernel", 10)) {
        if(LoadKernel(KERNEL_RAM_START))
            printk("Load Kernel Failed!\n");
        else
            printk("Load Kernel Successful!\n");
    } else if(MyStrCmp(commandline, "docinfo", 7)) {
        doc_info();
#if 0
    } else if(MyStrCmp(commandline, "showlast512", 11)) {
        ShowLast512Byte();
    } else if(MyStrCmp(commandline, "writerootfs", 11)) {
        WriteRootFs();
    } else if(MyStrCmp(commandline, "ldrrootfs", 9)) {
        doc_read_rootfs();
    } else if(MyStrCmp(commandline, "writemmi", 8)) {
        WriteMMI();
    } else if(MyStrCmp (commandline, "usbdn", 5)) {
        UsbDownloadImage();
    } else if(MyStrCmp (commandline, "writetat", 8)) {
        WriteTatTable((unsigned char*)(0xa0000000)) ;
    } else if(MyStrCmp (commandline, "readtat", 7)) {
        ReadTatTable((unsigned char*)(0xa0000000));
    } else if(MyStrCmp(commandline, "writelogo", 9)) {
        WriteLogo();
    } else if(MyStrCmp(commandline, "readlogo", 8)) {
        ReadLogo();
    } else if(MyStrCmp(commandline, "keytest", 7)) {
        keyTest();
    } else if(MyStrCmp(commandline, "dn_b", 4)) {
        unsigned int address = RAM_START;
        Download((u8*)(address));
    } else if(MyStrCmp(commandline, "dn_k", 4)) {
        unsigned int address = RAM_START;
        Download((u8*)(address));
    } else if(MyStrCmp(commandline, "dn_r", 4)) {
        unsigned int address = RAM_START;
        Download((u8*)(address));
    } else if(MyStrCmp(commandline, "dn_c", 4)) {
        unsigned int address = RAM_START;
        Download((u8*)(address));
    } else if(MyStrCmp(commandline, "dn_u", 4)) {
        unsigned int address = RAM_START;
        Download((u8*)(address));
    } else if(MyStrCmp(commandline, "dn_t", 4)) {
        unsigned int address = RAM_START;
        Download((u8*)(address));
    } else if(MyStrCmp(commandline, "dn_i", 4)) {
        unsigned int address = RAM_START;
        Download((u8*)(address));
    } else if(MyStrCmp(commandline, "dn", 2)) {
        ReadLocation = 2;
        ParseResult[0]=ParseNumber(commandline+ReadLocation,
                        &NextReadLocation, &(ReadValue[0]));
        if(ParseResult[0]==1){
            Download((u8*)(ReadValue[0]));
        } else
            printk("Error : Some arguments are missing.\n");
#endif
    } else if(MyStrCmp(commandline, "dump", 4)){
        ReadLocation = 4;
        ParseResult[0] = ParseNumber(commandline+ReadLocation,
                        &NextReadLocation, &(ReadValue[0]));
        ReadLocation += NextReadLocation;
        ParseResult[1] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[1]));
        if((ParseResult[0] == 1) && (ParseResult[1] == 1)){
            DumpMem((u32*)(ReadValue[0]), ReadValue[1]);
        } else printk("Error : Some arguments are missing.\n");

    } else if(MyStrCmp(commandline, "help", 4)) {
        ReadLocation = 4;
        PrintHelp();

    } else if(MyStrCmp(commandline, "jump", 4)){
        ReadLocation = 4;
        ParseResult[0] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[0]));
        if((ParseResult[0]==1)){
            printk("Code will jump to "); DebugOutputHex(ReadValue[0]);
            cli();
            Jump((u32*)(ReadValue[0]));
            sti();
        } else
            printk("Error : Some arguments are missing.\n");
    } else if(MyStrCmp(commandline, "memcpy", 6)){
        ReadLocation = 6;
        ParseResult[0] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[0]));
        ReadLocation += NextReadLocation;
        ParseResult[1] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[1]));
        ReadLocation += NextReadLocation;
        ParseResult[2] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[2]));
        if((ParseResult[0] == 1)
                && (ParseResult[1] == 1)
                && (ParseResult[2]==1)){
            MyMemCpyChar((u8*)(ReadValue[0]), (u8*)(ReadValue[1]), ReadValue[2]);
        } else printk("Error : Some arguments are missing.\n");

    } else if(MyStrCmp(commandline, "memcmp", 6)){
        ReadLocation = 6;
        ParseResult[0] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[0]));
        ReadLocation += NextReadLocation;
        ParseResult[1] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[1]));
        ReadLocation += NextReadLocation;
        ParseResult[2] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[2]));
        if((ParseResult[0]==1)
                && (ParseResult[1]==1)
                && (ParseResult[2]==1)){
            CmpMem((ReadValue[0]), (ReadValue[1]), ReadValue[2]);
        } else printk("Error : Some arguments are missing.\n");

    } else if(MyStrCmp(commandline, "memset", 6)){
        ReadLocation = 6;
        ParseResult[0] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[0]));
        ReadLocation += NextReadLocation;
        ParseResult[1] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[1]));
        ReadLocation += NextReadLocation;
        ParseResult[2] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[2]));
        if((ParseResult[0] == 1)
                && (ParseResult[1] == 1)
                && (ParseResult[2]==1)){
            MyMemSetChar((u8*)(ReadValue[0]), (u8)(ReadValue[1]), ReadValue[2]);
        } else printk("Error : Some arguments are missing.\n");
    } else if(MyStrCmp(commandline, "memsetw", 7)){
        ReadLocation = 7;
        ParseResult[0] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[0]));
        ReadLocation += NextReadLocation;
        ParseResult[1] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[1]));
        ReadLocation += NextReadLocation;
        ParseResult[2] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[2]));
        if((ParseResult[0] == 1)
                && (ParseResult[1] == 1)
                && (ParseResult[2]==1)){
            MyMemSet((u32*)(ReadValue[0]), (u32)(ReadValue[1]), ReadValue[2]);
        } else printk("Error : Some arguments are missing.\n");
#if 0
    } else if(MyStrCmp(commandline, "ram_test", 8)) {
        ReadLocation = 8;
        ParseResult[0] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[0]));
        ReadLocation += NextReadLocation;
        ParseResult[1] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[1]));
        if((ParseResult[0] == 1)
                && (ParseResult[1] == 1)){
            ram_test((u32*)(ReadValue[0]), ReadValue[1]);
        } else printk("Error : Some arguments are missing.\n");

    } else if(MyStrCmp(commandline, "reset", 5)){
        ReadLocation = 5;
        hw_reset();
#endif
    } else if(MyStrCmp(commandline, "ldr", 3)){
        ReadLocation = 3;
        ParseResult[0] = ParseFileName(commandline + ReadLocation,
                        &NextReadLocation, FileName);
        ReadLocation += NextReadLocation;
        ParseResult[1] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[0]));
        if((ParseResult[0] == 1)
                && (ParseResult[1] == 1)){
            load_memory(FileName, (u32)ReadValue[0]);
        } else printk("Error : Some arguments are missing.\n");

    } else if(MyStrCmp(commandline, "str", 3)){
        ReadLocation = 3;
        ParseResult[0] = ParseFileName(commandline + ReadLocation,
                        &NextReadLocation, FileName);
        ReadLocation += NextReadLocation;
        ParseResult[1] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[0]));
        ReadLocation += NextReadLocation;
        ParseResult[2] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[1]));
        if((ParseResult[0] == 1)
                && (ParseResult[1] == 1)
                && (ParseResult[2] == 1)){
            store_memory(FileName, (u32)ReadValue[0], (u32)ReadValue[1]);
        } else printk("Error : Some arguments are missing.\n");
#if 0
    } else if(MyStrCmp(commandline, "xdn_b", 5)) {
        unsigned int address = RAM_START;
        XModemReceive((u8*)(address));
    } else if(MyStrCmp(commandline, "xdn_i", 5)) {
        unsigned int address = RAM_START;
        XModemReceive((u8*)(address));
    } else if(MyStrCmp(commandline, "xdn_r", 5)) {
        unsigned int address = RAM_START;
        XModemReceive((u8*)(address));
    } else if(MyStrCmp(commandline, "xdn_c", 5)) {
        unsigned int address = RAM_START;
        XModemReceive((u8*)(address));
    } else if(MyStrCmp(commandline, "xdn_u", 5)) {
        unsigned int address = RAM_START;
        XModemReceive((u8*)(address));
    } else if(MyStrCmp(commandline, "xdn_t", 5)) {
        unsigned int address = RAM_START;
        XModemReceive((u8*)(address));
#endif
    } else if(MyStrCmp(commandline, "xdn_k", 5)) {
        XModemReceive((u8*)(KERNEL_RAM_START));
    } else if(MyStrCmp(commandline, "xdn ", 4)) {
        ReadLocation = 4;
        ParseResult[0] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[0]));
        if(ParseResult[0] == 1){
            XModemReceive((u8*)(ReadValue[0]));
        } else printk("Error : Some arguments are missing.\n");

    } else if(MyStrCmp(commandline, "xup ", 4)) {
        ReadLocation = 4;
        ParseResult[0] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[0]));
        ReadLocation += NextReadLocation;
        ParseResult[1] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[1]));
        if((ParseResult[0] == 1)
                && (ParseResult[1] == 1)){
            xmodemTransmit((u8*)ReadValue[0], ReadValue[1]);
        } else printk("Error : Some arguments are missing.\n");
#if 0
    } else if(MyStrCmp(commandline, "udn ", 4)) {
        ReadLocation = 4;
        ParseResult[0] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[0]));
        ReadLocation += NextReadLocation;
        ParseResult[1] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[1]));
        if((ParseResult[0]==1)&&(ParseResult[1] == 1)){
//   cli();
            udownload_cmd((u32 *)ReadValue[0], ReadValue[1]);
//   sti();
        } else printk("Error : Some arguments are missing.\n");

    } else if(MyStrCmp(commandline, "pmu_dump", 8)) {

        pcf50606_dump_registers();

    } else if(MyStrCmp(commandline, "pmu_set", 7)){
        /* usage: pmu_set <power_name> <voltage_value> */
        /*    - ex) pmu_set D1REG 11 */
        ReadLocation = 7;
        ParseResult[0] = ParseFileName(commandline + ReadLocation,
                        &NextReadLocation, FileName);
        ReadLocation += NextReadLocation;
        ParseResult[1] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[0]));
        if((ParseResult[0] == 1)
                && (ParseResult[1] == 1)){
            //load_memory(FileName, (u32)ReadValue[0]);
            pcf50606_pmu_set_voltage( FileName, ReadValue[0] );
        } else printk("Error : Some arguments are missing.\n");
    /* simon, debugging purpose */
    } else if(MyStrCmp(commandline, "pmu_pm_regs", 11)) {
        pcf50606_dump_pm_registers();
    } else if(MyStrCmp(commandline, "md5sum", 6)){
        ReadLocation = 6;
        ParseResult[0] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[0]));
        ReadLocation += NextReadLocation;
        ParseResult[1] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[1]));
        if((ParseResult[0] == 1)
                && (ParseResult[1] == 1)){
            md5_stream((char*) & ReadValue[0], ReadValue[1]);
        } else printk("Error : Some arguments are missing.\n");

    } else if(MyStrCmp(commandline, "verbose", 7)) {
        ReadLocation = 7;
        ParseResult[0] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[0]));
        if(ParseResult[0]==1){ /* input ok, now run the script */
            if( ReadValue[0] >= 1 )
                verbose_level = 2;
            else
                verbose_level = 0;
        } else SerialOutputString("Error : Some arguments are missing.\n");
    } else if(MyStrCmp(commandline, "halt", 4)) {
        ReadLocation = 4;
        ParseResult[0] = ParseNumber(commandline + ReadLocation,
                        &NextReadLocation, &(ReadValue[0]));
        if(ParseResult[0]==1){ /* input ok, now run the script */
            msleep(ReadValue[0]); /* in ms */
        } else SerialOutputString("Error : Some arguments are missing.\n");

    } else if(MyStrCmp(commandline, "status", 6)){
        ReadLocation = 6;
        printk("jiffies       : %d\n", jiffies);
        printk("jiffies_div10 : %d\n", jiffies_div10);
        ShowUsbPinState();
    } else if(MyStrCmp(commandline, "version", 7)){
        ShowVersion();
    } else if(MyStrCmp(commandline, "bcm_on2pc",9)) {
        bcm2121_on2pc();
    } else if(MyStrCmp(commandline, "bcm_download2pc", 15)) {
        bcm2121_download2pc();
    } else if(MyStrCmp(commandline, "bcm_active",10)) {
        bcm2121_active();
    } else if(MyStrCmp(commandline, "bcm_inactive",12)) {
        bcm2121_inactive();
#endif
    } else if(MyStrCmp(commandline, "boot", 4)) {
        BootKernel(0, 0);
    } else if(MyStrCmp ((const char *)commandline,  (const char *)"u", 1)){
        g_bWrite = 0;
        if (UsbDownloadImageEx()) {
            g_bWrite = 1;
            BootKernel(BOOT_ARG0, BOOT_ARG1);
        }
    } else {
        printk("*** Unknown command:  %s \n", commandline);
        PrintHelp();
    }
}

/*
  Jump to the pre-defined TAT in Flash
*/
void JumptoTAT(void)
{
    void (*theTAT)(void) = (void (*)(void))TAT_FLASH_START;
    theTAT();

    printk("Hey, the TAT returned! This should not happen.\r");
}

/*
  Jump to the pre-copied kernel in the RAM
*/
typedef struct BootInfo {
    unsigned int LoadAddress;
    unsigned int ImageBase;
    unsigned int ImageSize;
    unsigned int MemorySize;
    unsigned int WriteMagic;
    unsigned int Reserved1;
    unsigned int Reserved2;
    unsigned int Reserved3;
} BootInfo;

typedef struct PackageInfo {
    unsigned int    uReserved;
    void        *modules;
    unsigned int    uNumberOfModules;
    unsigned int    uPackageSize;
    char        *szCommandLine;
} PackageInfo;

void BootKernel(int arg0, int arg1)
{
    BootInfo *pLoadInfo = RAM_START;
    int *pEntry;
    void (*theKernel)(int , int );

    // find kernel entry
    pEntry = KERNEL_RAM_START + 0x3c;

    if ((*(int *)(*pEntry + KERNEL_RAM_START)) != 0x00004550) {
        printk("Kernel is not PE format!\n");
        return;
    }
    pEntry = (int*)(KERNEL_RAM_START + (*pEntry) + 0x28);
    theKernel = (*pEntry) + KERNEL_RAM_START;

    // set kernel arguments
    if (!arg1) {
        arg1 = pLoadInfo;
        pLoadInfo->WriteMagic = 0;
        pLoadInfo->MemorySize = 0x4000000;
        pLoadInfo->LoadAddress = KERNEL_RAM_START;
        pLoadInfo->ImageBase = 0xc0020000;
        pEntry = KERNEL_RAM_START + 0x8;
        pLoadInfo->ImageSize = ((PackageInfo *)(*pEntry + KERNEL_RAM_START))\
                             ->uPackageSize;
    }
    cli();
    theKernel(arg0,arg1);
    sti();

    printk("Hey, the kernel returned! This should not happen.\r");
}

void Jump(u32* address)
{
    void (*Func)(void)= (void(*)(void))address;

    (*Func)();

    /* To return to this point, code should save every registes used,
     * lr, etc. and return to the lr and should never modify the SP
     */
}

/*
 * copy the kernel Image and ramdisk from the flash to the ramdisk
 */
void LoadKernelAndRamdisk(void)
{
    //copy_block(KERNEL_RAM_START, KERNEL_FLASH_START, KERNEL_FLASH_LENGTH);
    //copy_block(0xa0000100, 0x1ee2000, 2); // ethan
}

void hw_reset(void)
{
    int start_time;
    start_time = TimerGetTime();
    while((TimerGetTime()-start_time) < 2);
    SerialInit(baud115k2);
    TimerInit();
// net_init();
}

/*
  Serial download, not implemented yet
*/
void Download(u8* address)
{
    serial_load( address );  /* fill out here */
}

int SplitRam(int addr, int nSize)
{
    int desAddr = 0xa0100000;
    int noCpy;
    int i;

    if (nSize <= 0) {
        printk("no boot loader data\n");
        return 0;
    }
    else if (nSize > 0x80000) {
        printk("too large size\n");
        return 0;
    }

    noCpy = (nSize + 0x1ff) >> 9;
    noCpy = noCpy > 0x400? 0x400: noCpy;

    for (i = 0; i < noCpy; i++) {
        memcpy(desAddr, addr, 0x200);
        desAddr += 0x200;
        memcpy(desAddr, addr, 0x200);
        desAddr += 0x200;
        addr += 0x200;
    }

    return 0xa0100000;
}

void PrintHelp(void)
{
    printk("\nHelp for Elastos2.0 Bootloader:\n\n");
    printk("* elastos [arg0 arg1]  Boot Elastos2.0 with optional kernel options\n");
    printk("* udl                  Update bootloader via xMODEM\n");
    //printk("* usbdn                Download kernel image to RAM by USB\n");
    //printk("                       and write to kernel partition on DOC\n" );
    printk("* u                    Download kernel image to RAM by USB and\n");
    //printk("                       run into kernel\n" );
// printk("* dn {add}             Download a file via yMODEM to the address of {add}\n");
// printk("* dn_k                 Download kernel file via yMODEM\n");
    printk("* xdn {add}            Download a file via xMODEM to the address of {add}\n");
    printk("* xdn_k                Download elastos.img via xMODEM\n");
    printk("* docinfo              Infomation of DiskOnChip\n");
    printk("* formatdoc            Format DOC. NOTE: Update loader after formatdoc\n");
    printk("* writekernel          Write Kernel(run xdn_k before)\n");
    printk("* loadkernel           Load Kernel\n");
    printk("* dump {add} {len}     Dump the contents of the memory\n");
    printk("* jump {add}           Brach to the given {add}\n");
    printk("* memset {add} {val} {len}\n");
    printk("                       Set the memory with given {val}\n");
    printk("* memcpy {dest_add} {source_add} {len}\n");
    printk("                       Copy memory from the source to the destination\n");
    printk("* memcmp {add1} {add2} {len}\n");
    printk("                       Compare contents of memory\n");
    printk("* ldr [-l : -h : -b] {add}\n");
    printk("                       Display [32:16:8]bits memory contents.\n");
    printk("* str [-l : -h : -b] {add} {val}\n");
    printk("                       Store [32:16:8]bits {val} on {add}\n");
    //printk("* pmu_dump [reg_name]  Dump the contents of each pmu related register\n");
    //printk("                       of pcf50606 chip.\n" );
    //printk("* pmu_set <power_name> <voltage>\n");
    //printk("                       "
//                    "power_name:<D1REG|D2REG|D3REG|DCD|DCDE|DCUD|IOREG|LPREG>\n" );
    //printk("                       voltage: 9 ~ 55\n" );
// printk("* writerootfs          Write  ROOTFS\n");
// printk("* Writemmi             Write  MMI\n");
    //printk("* bcm_on2pc            Power on BCM2121 and switch UART to PC\n");
    //printk("* bcm_download2pc      Set BCM2121 to download mode and switch UART to PC\n");
    //printk("* bcm_active           Wake BCM2121\n");
    //printk("* bcm_inactive         Put BCM2121 into sleep mode\n");
    printk("* help                 Show this messages\n");
}
